package com.baseutils;

import android.text.TextUtils;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 　　　　　　　　┏┓　　　┏┓+ + 　　　　　　　┏┛┻━━━┛┻┓ + + 　　　　　　　┃　　　　　　　┃ 　　　　　　　┃　　　━　　　┃ ++
 * + + + 　　　　　　 ████━████ ┃+ 　　　　　　　┃　　　　　　　┃ + 　　　　　　　┃　　　┻　　　┃
 * 　　　　　　　┃　　　　　　　┃ + + 　　　　　　　┗━┓　　　┏━┛ 　　　　　　　　　┃　　　┃ 　　　　　　　　　┃　　　┃ + + + +
 * 　　　　　　　　　┃　　　┃　　　　Code is far away from bug with the animal protecting
 * 　　　　　　　　　┃　　　┃ + 　　　　神兽保佑,代码无bug 　　　　　　　　　┃　　　┃ 　　　　　　　　　┃　　　┃　　+ 　　　　　　　　　┃　
 * 　　┗━━━┓ + + 　　　　　　　　　┃ 　　　　　　　┣┓ 　　　　　　　　　┃ 　　　　　　　┏┛ 　　　　　　　　　┗┓┓┏━┳┓┏┛ + +
 * + + 　　　　　　　　　　┃┫┫　┃┫┫ 　　　　　　　　　　┗┻┛　┗┻┛+ + + +
 * <p>
 * 线程管理类，项目普通耗时操作均使用此类执行线程
 * <p>
 * 如果想要直接调用，而不用线程执行的任何异常， 直接调用{@link #executeTaskWithoutException(Runnable)}或者
 * {@link #executeTaskWithoutException(Runnable, String)}
 * 但是要注意返回布尔类型的结果，如果返回true，说明线程执行成功，反之则失败
 * <p>
 * 如果不想直接调用，需要考虑线程执行异常，一般异常是因为线程池关闭，所以提交的任务不能被执行 调用
 * {@link #executeTask(Runnable, String)}或者 {@link #executeTask(Runnable)}
 *
 * @author Done
 * @date 2018/1/31
 */

public class ThreadManager {
	private static final String TAG = "ThreadManager";

	private volatile static ThreadManager instance;

	private ThreadPoolExecutor poolExecutor;

	private LinkedBlockingQueue<Runnable> threadQueue;

	private SmartThreadFactory smartThreadFactory;

	private SmartRejectHandler smartRejectHandler;

	private static final int CORE_SIZE = 50;

	private static final int MAXIMUM_POOL_SIZE = Integer.MAX_VALUE;

	private static final long KEEP_ALIVE_TIME = 0L;
	private long lastExcuteTime = 0;

	public static ThreadManager getInstance() {
		if (null == instance) {
			synchronized (ThreadManager.class) {
				if (null == instance) {
					instance = new ThreadManager();
				}
			}
		}
		return instance;
	}

	private ThreadManager() {
		threadQueue = new LinkedBlockingQueue<Runnable>();
		smartThreadFactory = new SmartThreadFactory();
		smartRejectHandler = new SmartRejectHandler();
		poolExecutor = new ThreadPoolExecutor(CORE_SIZE, MAXIMUM_POOL_SIZE,
				KEEP_ALIVE_TIME, TimeUnit.MILLISECONDS, threadQueue,
				smartThreadFactory, smartRejectHandler);
	}

	public synchronized void destroy() {
		if (poolExecutor != null && !poolExecutor.isShutdown()) {
			poolExecutor.shutdown();
		}
		if (threadQueue != null && threadQueue.size() > 0) {
			threadQueue.clear();
		}
		threadQueue = null;
		smartThreadFactory = null;
		smartRejectHandler = null;
		instance = null;
		// Runtime.getRuntime().gc();
	}

	public boolean executeTaskWithoutException(Runnable task, String threadName) {
		try {
			execute(task, threadName);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * execute task u know
	 *
	 * @param task
	 *            task what u want to execute
	 * @return if true execute success else failed
	 */
	public boolean executeTaskWithoutException(Runnable task) {
		try {
			execute(task);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	public Future submitTaskWithoutException(Callable task) {
		try {
			return submit(task);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public Future submitTaskWithoutException(Callable task, String threadName) {
		try {
			return submit(task, threadName);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public void executeTask(Runnable task, String threadName) throws Exception {
		execute(task, threadName);
	}

	public void executeTask(Runnable task) throws Exception {
		execute(task);
	}

	/**
	 * 每一秒执行一次
	 *
	 * @param limitSecond
	 *            限制多少秒执行一次
	 * @param task
	 * @throws Exception
	 */
	public void executeTaskEachSecond(int limitSecond, Runnable task)
			throws Exception {
		try {
			long currentTimeMillis = System.currentTimeMillis();
			if (lastExcuteTime == 0) {
				execute(task);
				lastExcuteTime = currentTimeMillis;
			} else {
				if (currentTimeMillis - lastExcuteTime > limitSecond * 1000) {
					execute(task);
					lastExcuteTime = currentTimeMillis;
				} else {
//					Log.e(TAG, "时间小于1秒: ");
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
//			Log.e(TAG,
//					"executeTaskEachSecondError: " + e.getMessage());
		}
	}

	public Future sumbitTask(Callable task, String threadName) throws Exception {
		return submit(task, threadName);
	}

	public Future sumbitTask(Callable task) throws Exception {
		return submit(task);
	}

	private void execute(Runnable runnable, String threadName) throws Exception {
		smartThreadFactory.setName(threadName);
		poolExecutor.execute(runnable);
	}

	/**
	 * execute your task u know
	 *
	 * @param task
	 *            task what u want to execute
	 * @throws Exception
	 *             RejectedExecutionHandler {@link SmartRejectHandler}
	 */
	private void execute(Runnable task) throws Exception {
		poolExecutor.execute(task);
	}

	private Future submit(Callable task, String threadName) throws Exception {
		smartThreadFactory.setName(threadName);
		return poolExecutor.submit(task);
	}

	/**
	 * submit task u know
	 *
	 * @param task
	 *            your task
	 * @return task's result
	 * @throws Exception
	 *             RejectedExecutionHandler {@link SmartRejectHandler}
	 */
	private Future submit(Callable task) throws Exception {
		return poolExecutor.submit(task);
	}

	static class SmartThreadFactory implements ThreadFactory {

		private static final AtomicInteger poolNumber = new AtomicInteger(1);
		private final ThreadGroup group;
		private final AtomicInteger threadNumber = new AtomicInteger(1);
		private String namePrefix;

		SmartThreadFactory() {
			SecurityManager s = System.getSecurityManager();
			group = (s != null) ? s.getThreadGroup() : Thread.currentThread()
					.getThreadGroup();
			namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
		}

		public void setName(String name) {
			if (!TextUtils.isEmpty(name)) {
				namePrefix = name;
			}
		}

		@Override
		public Thread newThread(Runnable r) {
			Thread t = new Thread(group, r, namePrefix
					+ threadNumber.getAndIncrement());
			if (t.isDaemon()) {
				t.setDaemon(false);
			}
			if (t.getPriority() != Thread.NORM_PRIORITY) {
				t.setPriority(Thread.NORM_PRIORITY);
			}
			return t;
		}
	}

	static class SmartRejectHandler implements RejectedExecutionHandler {

		@Override
		public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
			if (!executor.isShutdown()) {
				executor.execute(r);
			} else {
				throw new RejectedExecutionException("Task " + r.toString()
						+ " rejected from " + executor.toString()
						+ "\n pool is shutdown!");
			}
		}
	}
}
